
/**
 * 创建一个孪生节点，注意这不是一个构造函数，不能用new创建
 * @param {String|Node} tag 节点的tagName,或真节点
 * @param {Object} option {namespace:"",is:""}
 * @returns 
 */
globalThis.TwinNode = function(tag, option) {

  if (!tag) return null;

  //真节点
  let targetNode;
  //如果是已经创建好的Node，则直接赋值给真节点
  if (tag instanceof Node){
    targetNode = tag;
  } 
  //window并不是一个Node，但有一些与Node相似的属性和方法
  else if (window && tag === window) {
    targetNode = window;
  }
  //如果tag是下划线，则创建一个文档碎片
  else if (tag === "_"){
    targetNode = new DocumentFragment();
  } 
  //创建有名称空间的节点，常用于svg
  else if (option?.namespace) {
    targetNode = document.createElementNS(option.namespace, tag.toLowerCase());
  }
  else if(option?.is){
    targetNode = document.createElement(tag.toLowerCase(),{is:option.is});
    targetNode.setAttribute("is",option.is)
  }
  //无名称空间的节点
  else{
    targetNode = document.createElement(tag.toLowerCase());
  } 

  //创建一个孪生节点，用来操作真节点
  const proxy = new Proxy({}, {

    get: function (_, propertyName,twinNode) {

      //是否是一个孪生节点
      if (propertyName === "isTwinNode") {
        return true
      }

      //通过下标0获取真节点
      if (propertyName === "0") {
        return targetNode
      }

      //通过下标-1获取真节点的shadowRoot的孪生节点
      else if (propertyName === "-1") {
        return TwinNode(targetNode.shadowRoot)
      }

      //querySelector
      else if (propertyName === "q") {
        return function (selector) { 
          return TwinNode(targetNode.querySelector(selector)) 
        }
      }

      //querySelectorAll
      else if (propertyName === "qa") {
        return function (selector) {
          return Array.from(targetNode.querySelectorAll(selector))
            .map(item => TwinNode(item))
        }
      }

      //children
      else if (propertyName === "children") {
        return function () {
          return Array.from(targetNode.children)
            .map(item => TwinNode(item));
        }
      }

      //追加子节点
      else if (propertyName === "append") {

        return function (...children) {

          for (let child of children) {

            //1：舍弃非0的空值
            if (child === undefined || child === null || child === "") {
              continue;
            }

            //2：如果是真节点，则直接插入子节点
            if (child instanceof Node) {
              if(child instanceof HTMLTemplateElement){
                const clone = child.content.cloneNode(true);
                targetNode.append(clone);
              }
              else{
                targetNode.append(child);
              }
            }

            //3：孪生节点，取出真节点，并递归追加
            else if (child.isTwinNode) {
              twinNode.append(child[0]); 
            }

            //4：数组，展开并递归
            else if (child instanceof Array || child instanceof NodeList || child instanceof HTMLCollection) {
              for (let item of child) twinNode.append(item);
            }

            //5：函数，执行函数后递归
            else if (child.constructor === Function) {
              if(child.length===0){
                twinNode.append(child());
              }
              else{
                child(twinNode.append);
              }
            }

            //6：特殊正则表达式，配置id和class
            else if (child.constructor === RegExp) {
              //样本 /#my_id.class1.class2[enabled]/       
              const reg_id = /(?<=#)[a-zA-Z_][\w]*/g;         //匹配选择器中的id,要求id符合js变量命名规则，且不能出现$
              const reg_class = /(?<=\.)[a-zA-Z_][\w\-]*/g;   //匹配选择器中的class
              const reg_attr = /(?<=\[)[^\]]+/g;              //匹配属性
              //从正则中提取文本
              let str = child.toString();
              let end = str.lastIndexOf("/");
              str = str.substring(1, end);
              str = str.replace(/\\-/g,"-");
              //从正则表达式中提取最后一个id
              let ids = str.match(reg_id);
              if (ids?.length) {
                let id = ids[ids.length - 1];
                targetNode.id = id;
              }
              //从正则表达式中提取所有的类
              let classes = str.match(reg_class);
              if (classes?.length) {
                for (let item of classes) {
                  targetNode.classList.add(item);
                }
              }
              //从正则表达式中取出所有的attribute
              let attributes = str.match(reg_attr);
              if (attributes?.length) {
                for (let item of attributes) {
                  let arr = item.split("=");
                  let k = arr[0].trim();
                  if (arr.length === 1) {
                    targetNode.setAttribute(k.trim(),"");
                  }
                  else {
                    v = arr[1].trim();
                    targetNode.setAttribute(k, v);
                  }
                }
              }
            }

            //7：Object,配置属性和样式
            else if (child.constructor === Object) {
              for (let key in child) {
                let value = child[key];
                //如果value是一个函数，则配置事件
                if (value.constructor === Function) {
                  twinNode.on(key, value);
                }
                //如果key是style，则配置样式
                else if (key === "style") {
                  //如果是字符串形式的样式，则将其组装成对象形式
                  if (value.constructor === String) {
                    const arr = value.split(";");
                    for (let item of arr) {
                      if (item.trim() === "") continue;
                      const css = item.split(":");
                      const cssName = css[0].trim();
                      const cssValue = css[1].trim();
                      twinNode.css(cssName, cssValue);
                    }
                  }
                  //如果是对象形式的style，则直接组装
                  else if (value.constructor === Object) {
                    for (let cssName in value) {
                      twinNode.css(cssName, value[cssName]);
                    }
                  }
                }
                //配置attribute
                else {
                  twinNode.attr(key, value);
                }
      
              }
            }

            //其他情况当字符串处理
            else {
              targetNode.append(child.toString());
            }

          }

          return twinNode;

        }

      }

      //在当前节点的第一个子节点前插入
      else if (propertyName === "prepend"){
        return function(...children){
          for (let child of children) {
            if (child === undefined || child === null || child === "") {
              continue;
            }
            if (child instanceof Node) {
              if(child instanceof HTMLTemplateElement){
                const clone = child.content.cloneNode(true);
                targetNode.prepend(clone);
              }
              else{
                targetNode.prepend(child);
              }
            }
            else if (child.isTwinNode) {
              twinNode.prepend(child[0]); 
            }
            else {
              targetNode.prepend(child.toString());
            }
          }
          return twinNode;
        }
      }

      //追加到容器
      else if (propertyName === "appendTo") {
        return function (container) {
           container.append(targetNode);
           return twinNode;
        }
      }

      else if (propertyName==="prependTo"){
        return function (container) {
          container.prepend(targetNode);
          return twinNode;
       }
      }

      //清空容器并渲染
      else if (propertyName === "render") {
        return function (container) {
          if (container.isTwinNode) {
            container[0].innerHTML = "";
            twinNode.appendTo(container);
          }
          else if (container instanceof Node) {
            container.innerHTML = "";
            twinNode.appendTo(container);
          }
          return twinNode;
        }
      }

      //将节点映射到一个全局对象上，方便后面直接使用
      else if (propertyName === "ref") {
        return function (propertyName, obj = globalThis) {
          obj[propertyName] = twinNode;
          return twinNode;
        }
      }

      //获取或设置对象属性
      else if (propertyName === "prop") {
        return function (name, value) {
          if (!name) return twinNode;
          if (value === undefined) return targetNode[name];
          targetNode[name] = value;
          return twinNode;
        }
      }

      //添加或删除标签属性
      else if (propertyName === "attr") {
        return function (name, value) {
          if (!name) {
            return twinNode;
          }
          else if (value === undefined) {
            return targetNode.getAttribute(name);
          }
          else if (value === true) {
            targetNode.setAttribute(name, "");
            return twinNode;
          }
          else if (value === false) {
            targetNode.removeAttribute(name);
            return twinNode;
          }
          else {
            targetNode.setAttribute(name, value);
            return twinNode;
          }
        }
      }

      //配置或获取样式
      else if (propertyName === "css") {
        return function (name, value) {
          if (!name) {
            return twinNode;
          }
          if (value === undefined) {
            return getComputedStyle(targetNode, null)[name];
          }
          if (value.constructor === Number) {
            //数字会自动加px,因此z-index和opacity等一定要用字符串，而不是数字
            value = value + "px";
          }
          targetNode.style[name] = value;
          return twinNode;
        }
      }

      //设置css变量的值
      else if (propertyName==="var"){
        return function(name,value){
          if(value===false) targetNode.style.removeProperty(name);
          else targetNode.style.setProperty(name,value);
          return twinNode;
        }
      }

      //抛出事件
      else if (propertyName === "dispatchEvent") {
        return function (...types) {
          for (let type of types) {
            let event = new Event(type, { bubbles: true, cancelable: true, composed: true });
            targetNode.dispatchEvent(event);
          }
          return twinNode;
        }
      }

      //添加类名
      else if (propertyName === "class") {
        return function (className, condition) {
          if (!className) {
            return targetNode.classList;
          }
          if (condition === false) {
            targetNode.classList.remove(className);
          }
          else {
            targetNode.classList.add(className);
          }
          return twinNode;
        }
      }

      //添加事件处理程序
      else if (propertyName === "on") {
        return function (...args) {
          targetNode.addEventListener(...args);
          return twinNode;
        }
      }

      //移除事件处理程序
      else if (propertyName === "off") {
        return function (...args) {
          targetNode.removeEventListener(...args);
          return twinNode;
        }
      }

      //配置或获取value
      else if (propertyName === "value") {
        return function (value) { 
          return twinNode.prop("value", value);
        }
      }

      //配置或获取innerHTML
      else if (propertyName === "html") {
        return function (html) { 
          return twinNode.prop("innerHTML", html);
        }
      }

      //配置或获取innerText
      else if (propertyName === "text") {
        return function (text) { 
          return twinNode.prop("innerText", text);
        }
      }

      //parentElement
      else if (propertyName === "parent") {
        return function () { 
          return TwinNode(targetNode.parentElement);
        }
      }

      //insertAdjacentElement或insertAdjacentHTML
      else if (propertyName==="insert"){
        return function(value,where = "beforeend"){
          if(value===undefined){
            return twinNode;
          }
          if(value instanceof Node){
            targetNode.insertAdjacentElement(where,value);
          }
          else if(value.isTwinNode){
            targetNode.insertAdjacentElement(where,value[0]);
          }
          else{
            targetNode.insertAdjacentHTML(where,value);
          }
          return twinNode;
        }
      }

      //previousElementSibling
      else if (propertyName === "previous") {
        return function (value) {
          if (value === undefined) {
            return TwinNode(targetNode.previousElementSibling);
          }
          else {
            return twinNode.insert(value,"beforebegin");
          }
        }
      }

      //nextElementSibling
      else if (propertyName === "next") {
        return function (value) {
          if (value === undefined) {
            return TwinNode(targetNode.nextElementSibling);
          }
          else {
            return twinNode.insert(value,"afterend");
          }
        }
      }

      //上移
      else if (propertyName === "up") {
        return function(){
          let pre = targetNode.previousElementSibling;
          if(pre){
            pre.insertAdjacentElement("beforebegin",targetNode);
          }
          return twinNode;
        }
      }

      //下移
      else if (propertyName === "down") {
        return function(){
          let next = targetNode.nextElementSibling;
          if(next){
            next.insertAdjacentElement("afterend",targetNode);
          }
          return twinNode;
        }
      }

      //top,right,bottom,left
      else if (propertyName === "inset") {
        return function (...args) {
          //如果没有参数，则返回对象{top,right,bottom,left}
          if (args.length === 0) {
            let { top, right, bottom, left } = getComputedStyle(targetNode, null);
            let obj = {
              top: top === "auto" ? top : parseFloat(top),
              right: right === "auto" ? right : parseFloat(right),
              bottom: bottom === "auto" ? bottom : parseFloat(bottom),
              left: left === "auto" ? left : parseFloat(left)
            };
            return obj;
          }
          //如果只有一个参数,且args[0]是字符串，则直接配置真节点的inset样式
          else if (args.length === 1 && args[0].constructor === String) {
            targetNode.style.inset = args[0];
          }
          //其他情况按顺序配置top,right,bottom,left，不需要配置的地方用null,undefind,false配置
          else {
            let [top,right,bottom,left] = args;
            twinNode.top(top || top === 0 ? top : "auto");
            twinNode.right(right || right === 0 ? right : "auto");
            twinNode.bottom(bottom || bottom === 0 ? bottom : "auto");
            twinNode.left(left || left === 0 ? left : "auto");
          }
          return twinNode;
        }
      }

      //width,height
      else if (propertyName === "size") {
        return function (w, h) {
          if (arguments.length === 0) {
            let { width, height } = getComputedStyle(targetNode, null);
            let obj = {
              width: width === "auto" ? width : parseFloat(width),
              height: height === "auto" ? height : parseFloat(height)
            }
            return obj;
          }
          else {
            if (w || w === 0) twinNode.width(w);
            if (h || h === 0) twinNode.height(h);
            return twinNode;
          }
        }
      }

      //positon=absolute,并配置位置和大小
      else if (propertyName === "absolute") {
        return function (top, right, bottom, left, width, height,overflow) {
          twinNode.position("absolute")
            .inset(top, right, bottom, left)
            .size(width, height)
            .overflow(overflow || "auto");
          return twinNode;
        }
      }

      //将子节点配置为绝对定位，并配置位置和大小
      else if(propertyName==="absoluteChild"){
        return function (config) {
          if(config){
            for (let key in config) {
              let child = twinNode.q(key);
              if(child){
                child.absolute(...config[key]);
              }
            }
          }
          return twinNode;
        }
      }

      //positon=fixed,并配置位置和大小
      else if (propertyName==="fixed"){
        return function (top, right, bottom, left, width, height,overflow) {
          twinNode.position("fixed")
            .inset(top, right, bottom, left)
            .size(width, height)
            .overflow(overflow || "auto");
          return twinNode;
        }
      }

      //停靠
      else if (propertyName === "dock") {
        //where:停靠方向，可选值 top,right,bottom,left,center
        //size:宽度或高度
        //  当where=top或where=bottom时，size表示高度
        //  当where=left或where=right时，size表示宽度
        //offset:停靠方向的偏移量
        return function (where, size = 200, offset = 0) {
          switch (where) {
            case "top": return twinNode.fixed(offset, 0, null, 0,  null, size);
            case "right": return twinNode.fixed(0, offset, 0, null, size, null);
            case "bottom": return twinNode.fixed(null, 0, offset, 0, null, size);
            case "left": return twinNode.fixed(0, null, 0, offset, size, null);
            case "center": return twinNode.fixed(offset, offset, offset, offset, null, null);
            default: return twinNode;
          }
        }
      }

      //display=hide
      else if (propertyName === "hide") {
        return function () { 
          if(twinNode.display()!=="none"){
            twinNode.attr("old-display",twinNode.display())
            .display("none");
          }
          return twinNode;
        }
      }

      else if (propertyName === "show") {
        return function () {
          if (twinNode.display() === "none") {
            twinNode.display(twinNode.attr("old-display") || "block");
          }
          return twinNode;
        }
      }

      //弹性布局，并配置justifyContent,alignItems,alignContent
      else if(propertyName==="flexAlign"){
        return function (flexDirection, justifyContent, alignItems, alignContent) {
          targetNode.style.display="flex";
          //flexDirection
          let temp;
          switch(flexDirection){
            case 1:
            case "y":
            case "|":
            case "column":temp="column";break;
            default:temp=flexDirection;break;
          }
          targetNode.style.flexDirection = temp;
          //justifyContent
          temp;
          switch (justifyContent) {
            case undefined:
            case null:
            case 0: temp = "flex-start"; break;
            case 1: temp = "center"; break;
            case 2: temp = "flex-end"; break;
            default: temp = justifyContent; break;
          }
          targetNode.style.justifyContent = temp;
          //alignItems
          switch (alignItems) {
            case undefined:
            case null:
            case 0: temp = "flex-start"; break;
            case 1: temp = "center"; break;
            case 2: temp = "flex-end"; break;
            default: temp = alignItems; break;
          }
          targetNode.style.alignItems = temp;
          //alignContent
          switch (alignContent) {
            case undefined:
            case null:
            case 0: temp = "flex-start"; break;
            case 1: temp = "center"; break;
            case 2: temp = "flex-end"; break;
            default: temp = alignContent; break;
          }
          targetNode.style.alignContent = temp;
          return twinNode;
        }
      }

      //沿x轴方向弹性布局
      else if (propertyName === "flexX") {
        return function(justifyContent="flex-start",alignItems="center",alignContent="flex-start"){
          return twinNode.flexAlign("row",justifyContent,alignItems,alignContent)
        }
      }

      //沿y轴方向弹性布局
      else if (propertyName === "flexY") {
        return function(justifyContent,alignItems,alignContent){
          return twinNode.flexAlign("column",justifyContent,alignItems,alignContent)
        }
      }

      //分蛋糕
      else if(propertyName==="flexCake"){
        return function (direction,...itemSize) {        
          twinNode
            .flexAlign(direction,"flex-start","stretch")
            .overflow("hidden")
            .boxSizing("border-box");
          let children = twinNode.children();
          for (let i = 0; i < children.length; i++) {
            let size = itemSize[i];
            children[i]
              [direction==="row"?"width":"height"](0)  //主轴方向的尺寸设为0，交给flexGrow来拉伸
              .flex(`${size||0} 0 0px`)
              .overflow("hidden")
              .boxSizing("border-box");
          }
          return twinNode;
        }
      }

      else if(propertyName==="grid"){
        return function(rowCount,columnCount,cells){
          twinNode
            .display("grid")
            .gridTemplateRows(`repeat(${rowCount},1fr)`)
            .gridTemplateColumns(`repeat(${columnCount},1fr)`);
          for(let key in cells){
            let child = twinNode.q(key);
            if(!child) continue;
            let cell = cells[key];
            let r = cell[0]||1;
            let c = cell[1]||1;
            child.gridRow(`span ${r}`).gridColumn(`span ${c}`);
          }
          return twinNode;
        }
      }

      //执行函数，注意不管目标函数的返回值是什么，执行do后都会返回孪生节点
      else if (propertyName === "do") {
        return function (name, ...args) {
          if (name) return twinNode;
          targetNode[name](...args);
          return twinNode;
        }
      }

      //getBoundingClientRect的简写
      else if (propertyName === "rect") {
        return function () { return targetNode.getBoundingClientRect() }
      }

      //执行插件定义的函数
      else if (TwinNode.__definedMethods[propertyName]) {
        return function (...args) {
          let result = TwinNode.__definedMethods[propertyName].call(targetNode, ...args);
          return result === undefined ? twinNode : result;
        }
      }

      //其他情况全部是配置样式、属性、事件
      else {

        const first = propertyName[0];

        //如果第一字母是下划线，则配置标签属性
        //属性名规则：第一个下滑先会被删除，其余下滑线将替换为中划线
        if (first === "_") {
          let attributeName = propertyName.substring(1);
          attributeName = attributeName.split("_").join("-");
          return function (value) {
            return twinNode.attr(attributeName, value);
          }
        }

        //如果第一字母是大写字母，则配置事件
        else if (first.toLowerCase() !== first) {
          return function (...args) {
            return twinNode.on(propertyName.toLowerCase(), ...args)
          }
        }

        //如果第一个字母是小写字母，则配置样式
        else {
          return function (value) {
            return twinNode.css(propertyName, value);
          }
        }

      }

    }

  });

  return proxy;

}

/**
 * 定义一个标签函数
 * @param {String} tagName 标签名称
 * @param {String|undefined} option 如果是SVG则需要使用namespace
 * @returns 
 */
TwinNode.defineTag = function (tagName, option) {
  return function (...args) {
    const proxy = TwinNode(tagName, option);
    for (let arg of args) {
      if (arg !== undefined && arg !== null && arg !== "") {
        proxy.append(arg);
      }
    }
    return proxy;
  }
}

/**
 * 创建svg标签函数
 * @param {String} tagName 标签名
 * @returns 
 */
TwinNode.defineSvgTag = function (tagName) {
  return TwinNode.defineTag(tagName,{namespace:"http://www.w3.org/2000/svg"} );
}

/**
 * 标签函数工厂，请直接解构，如： const {div,span} = TwinNode.tags
 */
TwinNode.tags = new Proxy({}, {
  get(_, tagName) {
    if (tagName !== "_") {
      tagName = tagName.split("_").join("-");
    }
    return TwinNode.defineTag(tagName);
  }
})

/**
 * SVG标签工厂
 */
TwinNode.svgTags = new Proxy({}, {
  get(_, tagName) {
    if (tagName !== "_") {
      tagName = tagName.split("_").join("-");
    }
    return TwinNode.defineSvgTag(tagName);
  }
})

/**
 * 插件
 */
TwinNode.__definedMethods={};

/**
 * 定义插件函数
 * @param {*} methodName 函数名
 * @param {*} func 函数体
 */
TwinNode.defineMethod = function (methodName, func) {
  TwinNode.__definedMethods[methodName] = func;
}

/**
 * document.querySelector
 * @param {*} selector 
 * @returns TwinNode
 */
TwinNode.q = function (selector) {
  return TwinNode(document.querySelector(selector));
}

/**
 * document.querySelectorAll(
 * @param {*} selector 
 * @returns [TwinNode]
 */
TwinNode.qa = function (selector) {
  return Array.from(document.querySelectorAll(selector)).map(item => TwinNode(item));
}

//全局函数
globalThis.$$ = TwinNode;
